package com.starview.platform.data.type;

import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Set;

/**
 * An implementation of the @{link ITypeVisitor} that verifies the so-called occurs-check (that a
 * given type expression does not depend on a particular type variable).
 * 
 * The occurs check is an important part of type unification, as it prevents circular type
 * expressions being formed.
 * 
 * @author fgm
 * 
 */

public class OccursCheck extends AbstractTypeVisitor
{
  private final TypeVar var;
  private boolean found = false;
  private Set<String> visited = new HashSet<String>();

  public OccursCheck(TypeVar var)
  {
    this.var = var;
  }

  public static boolean occursIn(IType type, TypeVar var)
  {
    OccursCheck finder = new OccursCheck(var);
    if (!type.equals(var))
      type.accept(finder);
    return finder.found;
  }

  @Override
  public void visitTypeInterface(TypeInterfaceType t)
  {
    for (Entry<String, IType> entry : t.getAllFields().entrySet())
      entry.getValue().accept(this);
  }

  @Override
  public void visitTypeVar(TypeVar var)
  {
    IType type = var.deRef();

    if (type instanceof TypeVar) {
      var = (TypeVar) type;
      String varName = var.getVarName();

      if (!isExcluded(varName)) {
        if (!visited.contains(varName)) {
          visited.add(varName);
          if (var.equals(this.var))
            found = true;
          else
            for (ITypeConstraint cons : var)
              cons.accept(this);
        }
      }
    } else
      type.accept(this);
  }
}
